-- CareerM
-- Create by panyl
-- 宠物系别职业

module("CareerM", package.seeall);

-- 所有的系别、职业信息
local departments = {};
local careers = {};

-- 最高阶位
local MAX_RANK = 5;

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    -- 转换下department信息表
    local department_CSV = get_department_CSV();
    for i = 1, #department_CSV do
        departments[department_CSV[i].id] = department_CSV[i];
    end

    -- 转换下career信息表
    local career_CSV = get_career_CSV();
    for i = 1, #career_CSV do
        careers[career_CSV[i].id] = career_CSV[i];
    end

    department_CSV = {};
    career_CSV = {};
end

-- 模块初始化
function init()
    loadCsv();
end

-- 查看系别信息
function getDepartmentInfo(id, path)
    local m = departments[id];

    if not m or not path then
        return m;
    end

    return m[path];
end

-- 查看职业信息
function getCareerInfo(id, path)
    local m = careers[id];

    if not m or not path then
        return m;
    end

    return m[path];
end

-- 检查是否有可以学习、升级的称号
function checkCareer()
    local pet = ME.user:getActivePet();
    local list = getDepartmentList(pet);

    -- 遍历当前可学习的系别
    for _, departmentId in pairs(list) do
        local careerInfo = pet:getCareerData(departmentId);

        -- 遍历所有阶的称号
        for rank = 1, #careerInfo do
            -- 职业称号id
            local careerId = careerInfo[rank]["career"];
            local ok = true;

            -- 先提前判断一次消耗是否足够
            local cost = calcUpgradeCost(pet.classId, careerId);
            for key, value in pairs(cost) do
                if type(key) == "string" then
                    ok = ME.user.dbase:query(key, 0) >= value;
                elseif type(key) == "number" then
                    ok = ItemM.getAmount(ME.user, key) >= value;
                end
            end

            -- 如果可以升级
            if ok and true == canUpgrade(pet.classId, careerId) then
                return careerId;
            end
        end

        -- 判断下一阶位的是否可以学习
        local nextCareers = getCanLearnCareers(pet, departmentId);
        for _, careerId in pairs(nextCareers) do
            local ok = true;

            -- 先提前判断一次消耗是否足够
            local cost = calcJoinCost(pet.classId, careerId);
            for key, value in pairs(cost) do
                if type(key) == "string" then
                    ok = ME.user.dbase:query(key, 0) >= value;
                elseif type(key) == "number" then
                    ok = ItemM.getAmount(ME.user, key) >= value;
                end
            end

            -- 如果可以学习
            if ok and true == canJoin(pet.classId, careerId) then
                return careerId;
            end
        end
    end
end

-- 获取当前可学习的职业（首次学习时，仅返回一个初始职业）
function getCanLearnCareers(pet, departmentId)
    local careerData = pet:getCareerData(departmentId);
    local careerSize = #careerData;
    if careerSize == 0 then
        -- 默认取初始职业
        local initCareer = getDepartmentInfo(departmentId, "init_career");
        return { initCareer };
    end

    local info = careerData[careerSize];
    local nextCareers = getCanLearnNextCareers(pet, info["career"]);
    return nextCareers;
end

-- 获取某职业的下阶可选择职业
function getNextCareers(careerId)
    return getCareerInfo(careerId, "next") or {};
end

-- 获取可学习的下阶职业
function getCanLearnNextCareers(pet, careerId)
    local nextCareers = getCareerInfo(careerId, "next") or {};
    local ret = {};
    for _, id in pairs(nextCareers) do
        -- 仅返回符合条件的
        local result = FormulaM.invoke("CAN_JOIN_CAREER", pet, id);
        if true == result then
            table.insert(ret, id);
        end
    end
    return ret;
end

-- 获取可以升级的特性
function getFeatures(careerId)
    local ret = {};
    for index = 1, 3 do
        local info = getCareerInfo(careerId, "feature" .. index);
        if info and info["skill_id"] then
            table.insert(ret, index);
        end
    end

    -- 第四项为神器特殊附带的强化项
    local curArtifact = ArtifactM.getCarryArtifact();
    local cId = ArtifactM.query(curArtifact, "career_id") or -1;
    local maxLevel = FormulaM.invoke("CALC_CAREER_STRENGTHEN_LEVEL", curArtifact) or 0;

    if cId == careerId and maxLevel > 0 then
        local info = getCareerInfo(careerId, "feature" .. 4);
        if info and info["skill_id"] then
            table.insert(ret, 4);
        end
    end

    return ret;
end

----------------------
-- 获取宠物职业的特性信息
-- @param pet         宠物对象
-- @param careerId    职业id
-- @param featureId   特性id
-- @param isCurLevel  是否取当前等级的特性
function getFeatureInfo(pet, careerId, featureId, isCurLevel, isShowMaxLevel)
    local info = getCareerInfo(careerId);
    local departmentId = info["department"];
    local rank = info["rank"];
    local careerData = pet:getCareerData(departmentId);
    local data = careerData[rank] or {};

    local featureInfo = info["feature" .. featureId];
    local level;
    local limit = false;
    local ret = {};
    local myFeature = data["feature"] or {};

    level = myFeature[featureId] or 0;

    ret["cur_level"] = level;

    -- 显示最大等级效果
    if isShowMaxLevel then
        level = featureInfo["max_level"];
    end

    if level >= featureInfo["max_level"] then
        -- 满级了
        ret["skill_id"] = SkillM.findSkill(featureInfo["skill_id"], level);
        ret["top_level"] = 1;
    else
        if isCurLevel == true then
            -- 取当前等级的特性
            ret["skill_id"] = SkillM.findSkill(featureInfo["skill_id"], level);
        else
            -- 取下一等级的特性
            ret["skill_id"] = SkillM.findSkill(featureInfo["skill_id"], level + 1);
        end
    end

    if level == 0 then
        -- 标记这是新技能
        ret["is_new_skill"] = 1;
    end

    ret["max_level"] = featureInfo["max_level"];

    return ret;
end

-- 计算学习职业的消耗
function calcJoinCost(classId, careerId)
    local pet = ME.user.pets[classId];

    -- 公式去计算
    return FormulaM.invoke("CALC_JOIN_CAREER_COST", pet, careerId);
end

-- 升级职业消耗
function calcUpgradeCost(classId, careerId)
    local pet = ME.user.pets[classId];
    local info = getCareerInfo(careerId);
    local rank = info.rank;
    local departmentId = info.department;
    local careerData = pet:getCareerData(departmentId);

    assert(careerData[rank]);

    local level = careerData[rank]["level"];

    -- 交给公式计算

    return FormulaM.invoke("CALC_UPGRADE_CAREER_COST", pet, careerId, level);
end

-- 能否加入职业，需要判断前提职业是否符合
function canJoin(classId, careerId)
    local pet = ME.user.pets[classId];

    assert(pet);

    -- 1. 公式计算一次
    local result = FormulaM.invoke("CAN_JOIN_CAREER", pet, careerId);
    if true ~= result then
        trace("CareerM", tostring(result));
        return result;
    end

    -- 2. 是否已经激活了改系别（星级是否足够）
    local info = getCareerInfo(careerId);
    local departmentId = info["department"];
    if table.indexOf(getDepartmentList(pet), departmentId) <= 0 then
        return string.format(getLocStr("in_low_rank"), tostring(pet:query("name")), info.name);
    end

    -- 3. 前提职业是否符合
    local rank = getCareerInfo(careerId, "rank");
    local careerData = pet:getCareerData(departmentId);
    if type(careerData[rank]) == "table" then
        trace("CareerM", "宠物%d已经学习了%d阶职业%d", pet.classId, rank, careerData[rank]["career"]);
        return false;
    end

    -- 除了1阶的都需要前提职业
    if rank ~= 1 and type(careerData[rank - 1]) ~= "table" then
        trace("CareerM", "没有符合的前提职业。");
        return false;
    elseif type(careerData[rank - 1]) == "table" then
        local preCareer = careerData[rank - 1]["career"];
        local list = getNextCareers(preCareer);
        if table.indexOf(list, careerId) <= 0 then
            trace("CareerM", "前提职业不符合。");
            return false;
        end
    end

    -- 4. 判断消耗
    local cost = calcJoinCost(classId, careerId);
    local ok = true;
    for key, value in pairs(cost) do
        if type(key) == "string" then
            ok = ME.user.dbase:query(key, 0) >= value;
            if not ok then
                local name = FieldsM.getFieldName(key);
                return string.format(getLocStr("something_not_enough"), name);
            end
        elseif type(key) == "number" then
            ok = ItemM.getAmount(ME.user, key) >= value;
            if not ok then
                local name = ItemM.query(key, "name");
                return string.format(getLocStr("something_not_enough"), name);
            end
        end

        if not ok then
            trace("CareerM", "%s不足。", tostring(key));

            return ok;
        end
    end

    return true;
end

-- 判断能否升级职业
function canUpgrade(classId, careerId, feature, isNotify)
    local pet = ME.user.pets[classId];
    assert(pet);

    -- 1. 公式判断一次
    if not FormulaM.invoke("CAN_UPGRADE_CAREER", pet, careerId) then
        return false;
    end

    -- 2. 该阶位的职业是否已经学习了别的职业
    local departmentId = getCareerInfo(careerId, "department");
    local rank = getCareerInfo(careerId, "rank");
    local careerData = pet:getCareerData(departmentId);

    if not careerData[rank] or careerData[rank]["career"] ~= careerId then
        return false;
    end

    -- 职业等级是否已达上限
    if careerData[rank]["level"] >= getCareerInfo(careerId, "max_level") then
        return getLocStr("career_level_upper");
    end

    -- 5阶职业还要额外判断,当前神器开放等级
    if MAX_RANK == rank then
        -- 神器的开发等级
        -- 第四项为神器特殊附带的强化项
        local curArtifact = ArtifactM.getCarryArtifact();
        local cId = ArtifactM.query(curArtifact, "career_id") or -1;
        local strengthenLevel = FormulaM.invoke("CALC_CAREER_STRENGTHEN_LEVEL", curArtifact) or 0;
        local maxLevel = getCareerInfo(careerId, "max_level");
        maxLevel = maxLevel - 3;

        if cId == careerId then
            maxLevel = maxLevel + strengthenLevel;
        end

        if careerData[rank]["level"] >= maxLevel then
            return getLocStr("career_level_upper");
        end
    end

    -- 指定的特性是否已经满级
    if feature and feature > 0 then
        local info = getCareerInfo(careerId, "feature" .. feature) or {};
        local maxLevel = info["max_level"] or 0;
        local curLevel = careerData[rank]["feature"][feature] or 0;
        if curLevel >= maxLevel then
            return doAlert(getLocStr("career_level_upper"));
        end
    end

    -- 3. 判断消耗
    local cost = calcUpgradeCost(classId, careerId);
    local ok = true;

    for key, value in pairs(cost) do
        if type(key) == "string" then
            ok = ME.user.dbase:query(key, 0) >= value;
            if not ok then
                local name = FieldsM.getFieldName(key);
                return string.format(getLocStr("something_not_enough"), name);
            end

        elseif type(key) == "number" then
            ok = ItemM.getAmount(ME.user, key) >= value;
            if not ok then
                local name = ItemM.query(key, "name");
                return string.format(getLocStr("something_not_enough"), name);
            end
        end

        if not ok then
            trace("CareerM", "%s不足，不能升级职业%d", tostring(key), careerId);
            return ok;
        end
    end

    return true;
end

-- 本次迷宫是否加入过某个职业
function hasJoined(careerId)
    local pet = ME.user:getActivePet();
    if not pet then
        return false;
    end

    local departmentId = getCareerInfo(careerId, "department");
    local careerData = pet:getCareerData(departmentId);
    for _, info in pairs(careerData) do
        if info.career == careerId then
            return true;
        end
    end

    return false;
end

-- 学习某个职业
function joinCareer(classId, careerId)
    -- 判断一次
    if true ~= canJoin(classId, careerId) then
        return false;
    end

    -- 先做消耗
    local cost = calcJoinCost(classId, careerId);
    doCost(cost);

    local pet = ME.user.pets[classId];
    local info = getCareerInfo(careerId);
    local departmentId = info.department;
    local rank = info.rank;
    local careerData = pet:getCareerData();

    if not careerData[departmentId] then
        careerData[departmentId] = {};
    end

    careerData[departmentId][rank] = {
        ["career"] = careerId,
        ["level"] = 1,
        ["feature"] = {},
    };

    -- 自动学习特性
    local ret1;
    local ret2;
    local ret = {};
    for index = 1, 3 do
        local featureinfo = getCareerInfo(careerId, "feature" .. index);
        if featureinfo and featureinfo["auto"] == 1 then
            -- 自动学习一级特性
            careerData[departmentId][rank]["feature"][index] = 1;

            -- 奖励
            if type(featureinfo["bonus"]) == "string" and string.startWith(featureinfo["bonus"], "formula_") then
                local formulaName = string.sub(featureinfo["bonus"], string.len("formula_") + 1);
                ret1 = FormulaM.invoke(formulaName, 1, 1);
            else
                ret1 = FormulaM.invoke("CALC_CAREER_BONUS", ME.user, featureinfo["bonus"], 1);
            end

            ret = table.append(ret, ret1);
        end
    end

    -- 更新下系别职业数据
    pet:updateCareerData(careerData);

    -- 奖励
    if type(info["bonus"]) == "string" and string.startWith(info["bonus"], "formula_") then
        formulaName = string.sub(info["bonus"], string.len("formula_") + 1);
        ret2 = FormulaM.invoke(formulaName, 1, 1);
    else
        ret2 = FormulaM.invoke("CALC_CAREER_BONUS", ME.user, info["base_bonus"], 1);
    end


    ret = table.append(ret, ret2);

    -- 逐个执行奖励
    for _, bonus in pairs(ret) do
        BonusM.doBonus(bonus, "career_bonus");
    end

    -- 特殊效果处理
    local specialEffect = info.base_special_effect;
    if type(specialEffect) == "string" and string.len(specialEffect) > 0 then
        FormulaM.invoke(specialEffect, ME.user, careerId);
    end

    -- 抛出事件
    EventMgr.fire(event.JOIN_CAREER, { ["classId"] = careerId, ["careerId"] = careerId, ["ret"] = ret, });

    -- 魔之右眼：降低特定系的消耗属性
    local prop = PropM.combine(ME.user, "magic_eye", departmentId);
    if prop[3] > 0 then
        -- 触发特技发动事件
        EventMgr.fire(event.SPECIAL_SKILL_TRIGGER, { propId = prop[1], });
    end

    -- 降低副系消耗
    prop = PropM.combine(ME.user, "skill_assist_cost", 1);
    local orderList = getDepartmentList(pet);
    if prop[3] > 0 and table.indexOf(orderList, departmentId) ~= 1 then
        -- 触发特技发动事件
        EventMgr.fire(event.SPECIAL_SKILL_TRIGGER, { propId = prop[1] });
    end

    -- 客户端记录学习状态
    setLearnedStat(careerId);

    return true;
end

-- 升级职业
function upgradeCareer(careerId, feature)
    local pet = ME.user:getActivePet();
    local classId = pet.classId;

    -- 判断一把
    if not canUpgrade(classId, careerId, feature) then
        return false;
    end

    -- 先做消耗
    local cost = calcUpgradeCost(classId, careerId);
    doCost(cost);

    local pet = ME.user.pets[classId];
    local info = getCareerInfo(careerId);
    local departmentId = info["department"];
    local rank = info["rank"];
    local careerData = pet:getCareerData();
    local data = careerData[departmentId][rank];
    local bonus;

    -- 等级+1
    data["level"] = data["level"] + 1;

    local featureLevel = 0;

    -- 指定了特性
    if feature and feature > 0 then
        -- 特性级别+1
        data["feature"][feature] = tonumber(data["feature"][feature] or 0) + 1;
        featureLevel = data["feature"][feature];

        -- 奖励
        if info["feature" .. feature] then
            bonus = info["feature" .. feature]["bonus"];
        end
    else
        -- 找一个未满级的自动学习特性
        for index = 1, 3 do
            local featureInfo = info["feature" .. index] or {};
            local curLevel = data["feature"][index] or 0;
            local maxLevel = featureInfo["max_level"] or 0;
            if featureInfo["auto"] == 1 and curLevel < maxLevel then
                data["feature"][index] = curLevel + 1;
                featureLevel = data["feature"][index];

                -- 奖励
                if featureInfo["bonus"] then
                    bonus = featureInfo["bonus"];
                end

                -- 找到一个
                feature = index;

                break;
            end
        end
    end

    careerData[departmentId][rank] = data;
    pet:updateCareerData(careerData);

    local ret = {};

    -- 如果配置的是公式，调用公式计算
    local prefix = "formula_";
    if type(bonus) == "string" and string.startWith(bonus, prefix) then
        local formulaName = string.sub(bonus, string.len(prefix) + 1);
        ret = FormulaM.invoke(formulaName, featureLevel, feature);
    else
        ret = FormulaM.invoke("CALC_CAREER_BONUS", ME.user, bonus, data["level"]);
    end

    -- 给奖励
    for _, bonus in pairs(ret) do
        BonusM.doBonus(bonus, "upgrade_career_bonus");
    end

    -- 抛出事件
    EventMgr.fire(event.UPGRADE_CAREER, {
        ["feature"]  = feature,
        ["careerId"] = careerId,
        ["level"]    = data["level"],
        ["ret"]      = ret,
        ["pet"]      = pet,
    });

    -- 魔之右眼：降低特定系的消耗属性
    local prop = PropM.combine(ME.user, "magic_eye", departmentId);
    if prop[3] > 0 then
        -- 触发特技发动事件
        EventMgr.fire(event.SPECIAL_SKILL_TRIGGER, { propId = prop[1] });
    end

    -- 降低副系消耗
    prop = PropM.combine(ME.user, "skill_assist_cost", 1);
    local orderList = getDepartmentList(pet);
    if prop[3] > 0 and table.indexOf(orderList, departmentId) ~= 1 then
        -- 触发特技发动事件
        EventMgr.fire(event.SPECIAL_SKILL_TRIGGER, { propId = prop[1] });
    end

    return true;
end

-- 获取宠物职业技能
function getCareerSkills(pet)
    local ret = {};
    local careerData = pet:getCareerData();

    -- 遍历所有系别职业特性，算出技能
    for _, data in pairs(careerData) do
        -- 遍历所有职业
        for _, career in pairs(data) do
            -- 先加上基础特性
            ret = table.append(ret, getCareerInfo(career["career"], "base_feature") or {});

            -- 所有特性
            local feature = career["feature"];
            for id, level in pairs(feature) do
                local info = getCareerInfo(career["career"], "feature" .. id);
                if info and info["skill_id"] then
                    local skillId = SkillM.findSkill(info["skill_id"], level);
                    table.insert(ret, skillId);
                end
            end
        end
    end

    return ret;
end

-- 已经激活的系别（可学习）
function getDepartmentList(pet, rank)
    local order = PetM.query(pet.classId, "department_order");

    assert(#order >= 3);

    -- 初始只能学习自己的
    local depList = { order[1], };

    -- 看下是否有链接的冈布奥
    local lictorPets = ME.user.dbase:query("lictor_pets") or {};

    for _, id in pairs(lictorPets) do
        if LictorM.canDispatch(id) then
            local depId = PetM.query(id, "department_order")[1];

            if table.indexOf(depList, depId) == -1 then
                table.insert(depList, depId);
            end
        end
    end

    local isCan = canLearnDragonPower(pet.classId);
    if true == isCan then
        table.insert(depList, DEPARTMENT_DRAGON);
    end

    return depList;
end

-- 消耗
function doCost(cost)
    for classId, count in pairs(cost) do
        if type(classId) == "string" then
            ME.user:costAttrib(classId, count);
        else
            ItemM.costAmount(ME.user, classId, count);
        end
    end
end

-- 清除宠物系别职业信息
function clearPetCareer(pet)
    if not pet then
        pet = ME.user:getActivePet();
    end

    assert(pet);

    pet:updateCareerData({});
end

-- 还原宠物职业系别数据
function restoreCareerData(data)
    local ret = {};

    -- 遍历所有系别
    for id, info in pairs(data) do
        ret[id] = {};

        -- 遍历系别下的所有职业
        for rank, arr in pairs(info) do
            assert(#arr > 0);

            ret[id][rank] = {};

            -- 第一个数记录了职业id和等级（高位是id，低位是level）
            ret[id][rank]["career"] = bit.brshift(arr[1], 16);
            ret[id][rank]["level"] = bit.band(arr[1], 0xffff);
            ret[id][rank]["feature"] = {};

            -- 剩下的全是职业的特性
            for index = 2, #arr do
                -- 高位是特性id，低位是等级
                local feature = bit.brshift(arr[index], 16);
                ret[id][rank]["feature"][feature] = bit.band(arr[index], 0xffff);
            end
        end
    end

    return ret;
end

-------------------------------
-- 获取职业特性描述
-- @param classId     宠物id
-- @param careerId    职业id
-- @param isCurLevel  是否取当前等级的特性
function getFeatureDesc(classId, careerId, isCurLevel, isShowMaxLevel)
    -- 获取该职业的所有特性
    local pet = ME.user.pets[classId];
    local str = "";
    local isLearn;

    if isCurLevel == true then
        isLearn = false;
    else
        isLearn = true;
    end

    -- 如果是最高阶职业，需要显示基础特性
    local careerInfo = getCareerInfo(careerId);
    if careerInfo["rank"] == MAX_RANK then
        for _, skillId in pairs(careerInfo["base_feature"]) do
            local desc = SkillM.getSkillDesc(skillId, isLearn);
            if isShowMaxLevel then
                if string.len(desc) > 0 then
                    str = str .. desc .. "\n";
                end
            else
                str = str .. desc .. "\n";
            end
        end

        local features = CareerM.getFeatures(careerId);
        for _, featureId in pairs(features) do
            local info = CareerM.getFeatureInfo(pet, careerId, featureId, isCurLevel, isShowMaxLevel);
            local skillId = info["skill_id"];
            if skillId and info["is_new_skill"] ~= 1 then
                -- 仅显示已学习的特性
                local desc = SkillM.getSkillDesc(skillId, isLearn);
                if isShowMaxLevel then

                    if string.len(desc) > 0 then
                        if featureId == 4 then
                        else
                        str = str .. desc .. "\n";
                        end
                    end
                else
                    str = str .. desc .. "\n";
                end
            end
        end

        return str;
    end

    -- 未满阶的职业
    local features = CareerM.getFeatures(careerId);
    for _, featureId in pairs(features) do
        local info = CareerM.getFeatureInfo(pet, careerId, featureId, isCurLevel, isShowMaxLevel);
        local skillId = info["skill_id"];
        if skillId then
            local desc = SkillM.getSkillDesc(skillId, isLearn);
            if isShowMaxLevel and featureId ~= 4 then
                if string.len(desc) > 0 then
                    str = str .. desc .. "\n";
                end
            else
                str = str .. desc .. "\n";
            end
        end
    end

    return str;
end

---------------------------------
-- 获取职业特性的基础属性（攻击、魔力、生命、魔法）
-- @param classId     宠物id
-- @param careerId    职业id
-- @param isCurLevel  是否取当前等级的特性
-- @return 当前等级或下一等级职业的基础属性
function getFeatureBaseProps(classId, careerId, isCurLevel, isSkillUpdate2, isShowMaxLevel)
    -- 获取该职业的所有特性
    local pet = ME.user.pets[classId];

    local ret = {};

    -- 如果是最高阶职业，取基础特性
    local careerInfo = getCareerInfo(careerId);
    local features = CareerM.getFeatures(careerId);
    if careerInfo["rank"] == MAX_RANK then
        if isSkillUpdate2 ~= true then
            for _, skillId in pairs(careerInfo["base_feature"]) do
                local props = SkillM.getSkillBaseProps(skillId);
                ret = table.append(ret, props);
            end
        else
            -- 最高阶技能强化，需要取所有特性
            for _, featureId in pairs(features) do
                local info = CareerM.getFeatureInfo(pet, careerId, featureId, isCurLevel, isShowMaxLevel);
                local skillId = info["skill_id"];
                if skillId and info.is_new_skill ~= 1 then
                    -- 已经学过了才显示属性
                    local props = SkillM.getSkillBaseProps(skillId);
                    ret = table.append(ret, props);
                end
            end
        end
    else
        -- 非最高阶的职业，需要取所有特性
        for _, featureId in pairs(features) do
            local info = CareerM.getFeatureInfo(pet, careerId, featureId, isCurLevel, isShowMaxLevel);
            local skillId = info["skill_id"];
            if skillId then
                local props = SkillM.getSkillBaseProps(skillId);
                ret = table.append(ret, props);
            end
        end
    end

    if #ret <= 1 then
        return ret;
    end

    -- 合并同类项
    local temp = {};
    for _, prop in pairs(ret) do
        local index = table.indexOf(temp, prop);
        if index == -1 then
            table.insert(temp, prop);
        else
            temp[index] = { prop[1], prop[2], prop[3] + temp[index][3], prop[4] };
        end
    end

    return temp;
end

---------------------------------
-- 获取职业特性的属性（不包含基础属性）
-- @param classId     宠物id
-- @param careerId    职业id
-- @param isCurLevel  是否取当前等级的特性
-- @return 当前等级或下一等级职业的属性
function getFeatureProps(classId, careerId, isCurLevel)
    -- 获取该职业的所有特性
    local pet = ME.user.pets[classId];

    local ret = {};

    -- 如果是最高阶职业，取基础特性
    local careerInfo = getCareerInfo(careerId);
    if careerInfo["rank"] == MAX_RANK then
        for _, skillId in pairs(careerInfo["base_feature"]) do
            local props = SkillM.getSkillBaseProps(skillId, true);
            ret = table.append(ret, props);
        end
    else
        -- 非最高阶的职业，需要取所有特性
        local features = CareerM.getFeatures(careerId);
        for _, featureId in pairs(features) do
            local info = CareerM.getFeatureInfo(pet, careerId, featureId, isCurLevel);
            local skillId = info["skill_id"];
            if skillId then
                local props = SkillM.getSkillProps(skillId, true);
                ret = table.append(ret, props);
            end
        end
    end

    if #ret <= 1 then
        return ret;
    end

    -- 合并同类项
    local temp = {};
    for _, prop in pairs(ret) do
        local index = table.indexOf(temp, prop);
        if index == -1 then
            table.insert(temp, prop);
        else
            temp[index] = { prop[1], prop[2], prop[3] + temp[index][3], prop[4] };
        end
    end

    return temp;
end

-- 获取职业等级
function getCareerLevel(pet, careerId)
    local departmentId = getCareerInfo(careerId, "department");
    local careerData = pet:getCareerData(departmentId);
    if #careerData == 0 then
        return 0;
    end

    local careerList = table.values(careerData);
    for _, info in pairs(careerList) do
        if info["career"] == careerId then
            return info["level"];
        end
    end

    return 0;
end

-- 判断职业是否已满级
function isCareerMaxLevel(pet, careerId)
    local curLevel = getCareerLevel(pet, careerId);
    local maxLevel = getCareerInfo(careerId, "max_level");
    return curLevel >= maxLevel;
end

-- 判断特性是否已满级
function isFeatureMaxLevel(pet, careerId, featureId)
    local featureInfo = CareerM.getFeatureInfo(pet, careerId, featureId, true);
    if featureInfo["top_level"] == 1 then
        -- 特性已满级
        return true;
    end

    return false;
end

-- 获取当前地牢开放的职业最高阶位
function getDungeonMaxCareerRank(departmentId)
    if not DungeonM.isInDungeon() then
        return 0;
    end

    local dungeonId = DungeonM.getDungeonId();
    local maxRank = DungeonAreaM.query(dungeonId, "max_career_rank");
    if type(maxRank) ~= "number" then
        error("max_career_rank字段配置有误。");
        return 0;
    end

    maxRank = CareerMaxRankCheck(maxRank);

    -- 当前开放最大阶位与系别有关
    if departmentId ~= nil then
        maxRank = FormulaM.invoke("CALC_DUNGEON_OPEN_MAX_RNAK", departmentId, maxRank);
    end
    return maxRank;
end

-- 检查该阶位是否有可学技能,返回可学的最高阶位
function CareerMaxRankCheck(rank)
    if rank == 0 then
        return rank;
    end

    local pet = ME.user:getActivePet();
    for classId, brief in pairs(careers) do
        if rank == getCareerInfo(classId, "rank") then
            if FormulaM.invoke("CAN_JOIN_CAREER", pet, classId) == true then
                return rank;
            end
        end
    end

    return CareerMaxRankCheck(rank - 1);
end

-- 获取已满级的特性数量
function getLevelUpperFeatureNum(pet, careerId)
    local total = 0;
    local featureIds = CareerM.getFeatures(careerId);
    for _, id in pairs(featureIds) do
        local featureInfo = CareerM.getFeatureInfo(pet, careerId, id, true);

        local skillId = featureInfo["skill_id"];
        local skillInfo = SkillM.query(skillId);

        if featureInfo["top_level"] == 1 then
            total = total + 1;
        end
    end

    return total;
end

-----------------------------------------------
-- 获取某一系别的所有称号
-- @param department
function getIdsByDepartment(department)
    local ret = {};
    for classId, brief in pairs(careers) do
        -- 获取物品详细信息
        local group = getCareerInfo(classId, "department");
        -- 找出符合要求的物品
        if department == group then
            table.insert(ret, classId);
        end
    end
    return ret;
end

----------------------------------------------
-- 获取某一个系别的名称
function getDepartmentName(departmentId)
    local str = spliceLocStr(CareerM.getDepartmentInfo(departmentId, "name"),
                       getLocStr("career_department"));

    return str;
end

function updateLearnedStat(learned_career_stat)
    if not learned_career_stat then
        return;
    end

    local buffer = Buffer.create(learned_career_stat);

    local offset = 1;
    local id = 0;
    local size = Buffer.size(buffer);

    -- 学习过的称号
    local learnedCareer = {};

    while (offset <= size) do
        -- 学习过的称号
        local state = Buffer.getu8(buffer, offset);

        if CareerM.getCareerInfo(id) and state > 0 then
            learnedCareer[id] = state;
        end

        -- 下一个
        id = id + 1;
        offset = offset + 1;
    end

    ME.user.dbase:set("learned_career_data", learnedCareer);
end

-- 职业是否学习过
function setLearnedStat(careerId)
    local learnedCareer = ME.user.dbase:query("learned_career_data", {});
    ME.user.dbase:set("learned_career_data", learnedCareer);

    learnedCareer[careerId] = 1;
end

-- 查询是否学习过该职业
function hasLearnedCareer(careerId)
    local learnedCareer = ME.user.dbase:query("learned_career_data", {});

    if learnedCareer[careerId] then
        return true;
    else
        return false;
    end
end

function canLearnDragonPower(classId)
    local dragonPower = DungeonM.getDungeonMixedField("dragon_power");

    if dragonPower == 1 then
        return true;
    end

    local canLearn = FormulaM.invoke("CALC_LEARN_DRAGON_POWER", classId);
    return canLearn;
end

-- 获取配置表
function getTable()
    return table.deepcopy(careers);
end

-- 验证客户端打包称号数据
function packCareerData(data)
    local ret = { __mapping = 1, };
    for id, info in pairs(data) do
        for _, m in pairs(info) do
            m.feature = to_mapping(m.feature);
        end

        ret[id] = to_mapping(info);
    end

    return ret;
end

-- 不同系的称号总称
function getCareerTitle(careerId)
    return getLocStr("career_title_" .. careerId);
end
